/*
 * Copyright (c) 2004 Your Corporation. All Rights Reserved.
 */
package com.talios.jira.browser;

import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.usageView.UsageInfo;

import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * @jira INTPLUG-19 Find issues in project
 */
public class IssueSearcher implements Runnable {
    private static final Logger LOGGER = Logger.getInstance("Jira Browser");

    private JiraItem item;
    private Set issueReferences = new HashSet();
    private Project project;

    public IssueSearcher(Project project, JiraItem item) {
        this.project = project;
        this.item = item;
    }

    public JiraItem getItem() {
        return item;
    }

    public Set getIssueReferences() {
        return issueReferences;
    }

    /**
     * @jira INTPLUG-19 Find issues in project
     */
    public void run() {
        ModuleManager mm = ModuleManager.getInstance(project);

        Module[] modules = mm.getModules();
        for (Module module : modules) {
            LOGGER.info("Found module: " + module.getName());
            ModuleRootManager mrm = ModuleRootManager.getInstance(module);
            VirtualFile[] sourceRoots = mrm.getSourceRoots();

            for (VirtualFile sourceRoot : sourceRoots) {
                findIssuesInDirectory(item, issueReferences, sourceRoot);
            }
        }
    }

    /**
     * @jira INTPLUG-19 Find issues in project
     */
    private Set findIssuesInDirectory(JiraItem item, Set issueReferences, VirtualFile sourceRoot) {
        PsiManager pm = PsiManager.getInstance(project);
        VirtualFile[] sourceFiles = sourceRoot.getChildren();

        for (VirtualFile sourceFile : sourceFiles) {
            if (sourceFile.isDirectory()) {
                findIssuesInDirectory(item, issueReferences, sourceFile);
            } else {

                PsiFile psiModuleFile = pm.findFile(sourceFile);

                // Only search Java files for annotations
                if (psiModuleFile != null && psiModuleFile.getLanguage() instanceof JavaLanguage) {
                    Set fileIssueReferences = findIssueInChildren(item, sourceFile, psiModuleFile);
                    if (!fileIssueReferences.isEmpty()) {
                        issueReferences.addAll(fileIssueReferences);
                    }
                }
            }

        }
        return issueReferences;
    }

    /**
     * @jira INTPLUG-19 Find issues in project
     */
    private Set findIssueInChildren(JiraItem item, VirtualFile virtualFile, PsiElement firstChild) {
        boolean issueFound = false;
        Set issueReferences = new HashSet();

        PsiElement[] children = firstChild.getChildren();
        for (PsiElement child : children) {
            if (child instanceof PsiComment) {
                Pattern issueTagPattern = Pattern.compile("@jira\\s" + item.getKey() + "\\s");
                boolean isIssueFound = issueTagPattern.matcher(child.getText()).find();
                issueFound = issueFound || isIssueFound;

                if (isIssueFound) {
                    LOGGER.info("Issue " + item.getKey() + " found in file: " + virtualFile.getName());

                    for (int j = 0; j < child.getChildren().length; j++) {
                        PsiElement psiElement = child.getChildren()[j];
                        if (psiElement instanceof PsiDocTag && issueTagPattern.matcher(psiElement.getText()).find()) {
                            issueReferences.add(new UsageInfo(psiElement));
                        }
                    }

//                        issueReferences.add(new UsageInfo(child));
                }

            } else {
                Set localIssueReferences = findIssueInChildren(item, virtualFile, child);
                issueFound = issueFound || !localIssueReferences.isEmpty();

                if (!localIssueReferences.isEmpty()) {
                    issueReferences.addAll(localIssueReferences);
                }
            }
        }

        return issueReferences;
    }

}